<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>默认的图片节点</title>
</head>
<body>
  <div id="mountNode"></div>
  <div id="mountNode1"></div>
  <script src="../build/g6.js"></script>
  <script>

const edgeTypeColorMap = {
  type1: ['#531dab', '#391085', '#391085'],
  type2: ['#d9d9d9', '#bfbfbf', '#8c8c8c'],
  type3: ['#d3adf7', '#b37feb', '#9254de']
}

const generateArrow = (lineWidth) => {
  let width = (lineWidth * 4) / 3;
  return [
    ['M', lineWidth / 2, 0],
    ['L', -width * 1.5, -width * 1.5],
    ['L', -width * 1.5, width * 1.5],
    // ['A', radius, radius, 0, 0, 1, -width / 2, halfHeight],
    ['Z']
  ];
}

const defaultConf = {
  style: {
    lineAppendWidth: 5,
    lineDash: [0, 0],
    lineDashOffset: 0,
    endArrow: true,
    opacity: 1,
    labelCfg: {
      style: {
        fillOpacity: 1
      }
    }
  },
  /**
   * 绘制边
   * @override
   * @param  {Object} cfg   边的配置项
   * @param  {G.Group} group 边的容器
   * @return {G.Shape} 图形
   */
  drawShape(cfg, group) {
    const item = group.get('item')
    const shapeStyle = this.getShapeStyle(cfg, item);
    const shape = group.addShape('path', {
      className: 'edge-path',
      attrs: shapeStyle
    });
    return shape;
  },
  drawLabel(cfg, group) {
    const labelCfg = cfg.labelCfg || {}
    const labelStyle = this.getLabelStyle(cfg, labelCfg, group)
    const text = group.addShape('text', {
      attrs: {
        ...labelStyle,
        text: cfg.label,
        fontSize: 12,
        fill: '#404040',
        cursor: 'pointer'
      },
      className: 'edge-label'
    })

    return text 
  },

  /**
   * 获取图形的配置项
   * @internal 仅在定义这一类节点使用，用户创建和更新节点
   * @param  {Object} cfg 节点的配置项
   * @return {Object} 图形的配置项
   */
  getShapeStyle(cfg, item) {
    const { startPoint, endPoint } = cfg
    const type = item.get('type')

    const defaultStyle =  this.getStateStyle('default', true, item)

    
    if(type === 'node') {
      return Object.assign({}, cfg.style, defaultStyle);
    }

    const controlPoints = this.getControlPoints(cfg);
    let points = [ startPoint ]; // 添加起始点
    // 添加控制点
    if (controlPoints) {
      points = points.concat(controlPoints);
    }
    // 添加结束点
    points.push(endPoint);
    const path = this.getPath(points);

    const style = Object.assign({}, { path }, cfg.style, defaultStyle);
    return style;
  },
  getControlPoints(cfg) {
    let controlPoints = cfg.controlPoints; // 指定controlPoints

    if (!controlPoints || !controlPoints.length) {
      const { startPoint, endPoint } = cfg;
      const innerPoint = G6.Util.getControlPoint(startPoint, endPoint, 0.5, cfg.edgeOffset || 30);
      controlPoints = [ innerPoint ];
    }
    return controlPoints;
  },
  /**
   * 获取三次贝塞尔曲线的path
   *
   * @param {array} points 起始点和两个控制点
   * @returns
   */
  getPath(points) {
    const path = [];
    path.push([ 'M', points[0].x, points[0].y ]);
    path.push([ 'Q', points[1].x, points[1].y, points[2].x, points[2].y ]);
    return path;
  },
  /**
   * 根据不同状态，获取不同状态下的样式值
   * @param {string} name 
   * @param {string} value 
   * @param {Item} item 
   */
  getStateStyle(name, value, item) {
    const model = item.getModel()
    const { style = {} } = model

    const defaultStyle = Object.assign({}, this.style)

    // 更新颜色
    return {
      ...defaultStyle,
      lineWidth: 1,
      stroke: edgeTypeColorMap[model.edgeType] && edgeTypeColorMap[model.edgeType][0],
      ...style
    }
  },
  /**
   * 拖动时更新path及边的label
   *
   * @param {object} cfg 边的model
   * @param {Edge} item 边的实例
   */
  update(cfg, item) {
    const { data, style, 
      startPoint, endPoint, labelCfg = {} } = cfg
    const group = item.getContainer()
    const model = data || cfg

    let arrowWidth = 2

    const defaultStyle = Object.assign({}, this.style, {
      lineWidth: 1,
      stroke: edgeTypeColorMap[model.edgeType] && edgeTypeColorMap[model.edgeType][0],
      endArrow: {
        path: generateArrow(arrowWidth),
      }
    }, style)

    const { opacity, onlyHideText } = defaultStyle

    // 更新 path
    const keyShape = item.getKeyShape();

    const controlPoints = this.getControlPoints(cfg);

    keyShape.attr({
      path: [
        ['M', startPoint.x, startPoint.y],
        ['Q', controlPoints[0].x, controlPoints[0].y, endPoint.x, endPoint.y]
      ],
      ...defaultStyle
    });

    const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
    const text = group.findByClassName('edge-label');
    const attrs = _.omit({
      ...labelStyle,
      fillOpacity: onlyHideText ? 0 : opacity === 0 ? opacity : 1,
      fill: '#404040'
    }, 'stroke')
    if(text) {
      text.resetMatrix();
      text.attr(attrs);
    }
  }
};

G6.registerEdge('quadratic-label-edge', defaultConf, 'quadratic');

// let nodes = []
//     for(let i = 0; i < 500; i++) {
//       nodes.push({
//         id: `node-${i}`,
//         label: `label-${i}`,
//         x: Math.random() * 650,
//         y: Math.random() * 550
//       })
//     }
    const data = {
      nodes: [
        {
          id: 'node1',
          label: 'node1'
        },
        {
          id: 'node2',
          label: 'node2'
        }
      ],
      edges: [
        {
          source: 'node1',
          target: 'node2',
          edgeType: 'type1'
        },
        {
          source: 'node2',
          target: 'node1',
          edgeType: 'type2'
        },
        {
          source: 'node1',
          target: 'node2',
          edgeType: 'type3',
          edgeOffset: -10
        }
      ]
    }

    const graph = new G6.Graph({
      container: 'mountNode',
      width: 800,
      height: 600,
      defaultNode: {
        shape: 'rect',
        size: [60, 30],
        color: 'green',
        style: {
          // fill: 'red',
          // stroke: '#eaff8f',
          lineWidth: 5
        },
        labelCfg: {
          style: {
            fill: '#9254de',
            fontSize: 18
          }
        },
        linkPoints: {
          top: true,
          bottom: true,
          left: true,
          right: true,
          size: 5,
          fill: '#fff'
        }
      },
      defaultEdge: {
        shape: 'quadratic-label-edge'
      },
      nodeStateStyles: {
        hover: {
          fill: '#d3adf7'
        }
      },
      modes: {
        default: ['drag-canvas', 'drag-node', {
          type: 'brush-select',
          trigger: 'ctrl'
        }]
      }
    })

    graph.data(data)
    graph.render()

    graph.on('node:click', evt => {
      const { item } = evt
      graph.setItemState(item, 'select', true)
      graph.updateItem(item, {
        size: [ 260, 130],
        style: {
          opacity: 0.6
        },
        preRect: {
          fill: 'blue'
        },
        linkPoints: {
          fill: '#fff',
          size: 5
        },
        stateIcon: {
          img: 'https://gw.alipayobjects.com/zos/basement_prod/c781088a-c635-452a-940c-0173663456d4.svg'
        }
      })
    })

    graph.on('node:mouseenter', evt => {
      const { item } = evt
      graph.setItemState(item, 'hover', true)
    })

    graph.on('node:mouseleave', evt => {
      const { item } = evt
      graph.setItemState(item, 'hover', false)
    })

    // graph.addItem('group', {
    //   nodes: ['image', 'triangle'],
    //   type: 'rect',
    //   zIndex: 0,
    //   title: {
    //     text: '名称'
    //   }
    // })
  </script>
</body>
</html>